using System;
using Microsoft.Xna.Framework;

namespace Oops.Xna.Framework
{
	/// <summary>
	/// An extension to the <see cref="System.Random" /> structure. 
	/// </summary>
	public static class RandomExtension
	{
		/// <summary>
		/// Returns a random number between 0.0f and 1.0f.
		/// </summary>
		/// <param name="random">A <see cref="System.Random"/> reference.</param>
		/// <returns>A single-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns>
		public static float NextSingle(this Random random)
		{
			return (float)random.NextDouble();
		}

		/// <summary>
		/// Returns a nonnegative random number less than the specified maximum.
		/// </summary>
		/// <param name="random">A <see cref="System.Random"/> reference.</param>
		/// <param name="max">The exclusive upper bound of the random number to be generated. <paramref name="max"/> must be greater than or equal to zero.</param>
		/// <returns>A single-precision floating point number greater than or equal to zero, and less than <paramref name="max"/>; that is, the range of return values includes zero but not <paramref name="max"/>.</returns>
		public static float NextSingle(this Random random, float max)
		{
			return (float)random.NextDouble() * max;
		}

		/// <summary>
		/// Returns a random number within a specified range.
		/// </summary>
		/// <param name="random">A <see cref="System.Random"/> reference.</param>
		/// <param name="min">The inclusive lower bound of the random number returned.</param>
		/// <param name="max">The exclusive upper bound of the random number returned. <paramref name="max"/> must be greater than or equal to <paramref name="min"/>.</param>
		/// <returns>A single-precision floating point number greater than or equal to <paramref name="min"/> and less than <paramref name="max"/>; that is, the range of return values includes <paramref name="min"/> but not <paramref name="max"/>. If minValue equals <paramref name="max"/>, <paramref name="min"/> is returned.</returns>
		public static float NextSingle(this Random random, float min, float max)
		{
			return (float)random.NextDouble() * (max - min) + min;
		}

		/// <summary>
		/// Returns a random number within a specified range.
		/// </summary>
		/// <param name="random">A <see cref="System.Random"/> reference.</param>
		/// <param name="min">The inclusive lower bound of the random number returned.</param>
		/// <param name="max">The exclusive upper bound of the random number returned. <paramref name="max"/> must be greater than or equal to <paramref name="min"/>.</param>
		/// <returns>A double-precision floating point number greater than or equal to <paramref name="min"/> and less than <paramref name="max"/>; that is, the range of return values includes <paramref name="min"/> but not <paramref name="max"/>. If minValue equals <paramref name="max"/>, <paramref name="min"/> is returned.</returns>
		public static double NextDouble(this Random random, double min, double max)
		{
			return random.NextDouble() * (max - min) + min;
		}

		public static Vector3 NextVector3(this Random random, float min, float max)
		{
			// Call overloaded method.
			return random.NextVector3(min, max, min, max, min, max);
		}

		public static Vector3 NextVector3(this Random random, float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
		{
			Vector3 result = new Vector3();
			result.X = (float)random.NextDouble() * (maxX - minX) + minX;
			result.Y = (float)random.NextDouble() * (maxY - minY) + minY;
			result.Z = (float)random.NextDouble() * (maxZ - minZ) + minZ;

			return result;
		}

		public static Quaternion NextQuaternion(this Random random, float min, float max)
		{
			// Call overloaded method.
			return random.NextQuaternion(min, max, min, max, min, max);
		}

		public static Quaternion NextQuaternion(this Random random, float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
		{
			Vector3 axis = new Vector3();
			axis.X = (float)random.NextDouble() * (maxX - minX) + minX;
			axis.Y = (float)random.NextDouble() * (maxY - minY) + minY;
			axis.Z = (float)random.NextDouble() * (maxZ - minZ) + minZ;

			return Quaternion.CreateFromAxisAngle(axis, 0.0f);
		}

		public static float NextSingleClamped(this Random random)
		{
			return random.NextSingle() - random.NextSingle();
		}
	}
}
